#!/usr/bin/perl -w

# The setup.* routines will eventually be folded into larger scripts 
# that configure the cluster or an application as a whole.
#
# This one will grab some ports for cluster daemons if they are not
# already defined. The return values are:
# 0 - ports allocated as required
# 1 - no work was needed
# 2 - an error has occured - see STDERR

BEGIN {
	if(!exists($::ENV{TRUECL_CODE})) {
		die("Missing TRUECL_CODE environment variable.");
	}
	if(!exists($::ENV{TRUECL_LOG})) {
		die("Missing TRUECL_LOG environment variable.");
	}
	if(!exists($::ENV{TRUECL_CFG})) {
		die("Missing TRUECL_CFG environment variable.");
	}
	if(! -d "$::ENV{TRUECL_CODE}/clreqd_modules") {
		die("Missing clreqd_modules directory in $::ENV{TRUECL_CODE}.");
	}
	push @::INC,"$::ENV{TRUECL_CODE}/lib";
	$::PKI=1;
	eval "use Crypt::RSA;";
	$::PKI=0 if $@;
}

$::PKI=1;
if($::PKI==1 && exists($::ENV{TRUECL_PKI})) {
	$::PKI=$::ENV{TRUECL_PKI};
}

sub usage {
	print "
Usage: setup.daemon-ports [--force] [--timeout N] [--parallel] [--width N]
";
	exit 1;
}

use strict;
use Getopt::Long;
use common_requests;
use clreqdxml;
use cllogging;
use Data::Dumper;

$::ARG_timeout=10;
$::ARG_force=0;
$::ARG_width=80;
$::ARG_parallel=0;

if(!GetOptions(
	"timeout=i"	=> \$::ARG_timeout,
	"width=i"	=> \$::ARG_width,
	"parallel"	=> \$::ARG_parallel,
	"force"		=> \$::ARG_force,
)) {
	usage;
}


#########################################################################
# Put in command line argument processing here and a usage msg.		#
#########################################################################

$::LOGGER=new cllogging("-",$::ARG_width);

if(!defined($::LOGGER)) {
	print STDERR "Error: Unable to initialise logging object.\n";
	exit 1;
}

$::LOGGER->set_verbose;
$::CLREQD=new clreqdxml("$::ENV{TRUECL_CFG}/clreqd.xml");
if(!defined($::CLREQD)) {
	$::LOGGER->msg("error","Unable to open/parse file $::ENV{TRUECL_CFG}/clreqd.xml - aborting.");
	$::LOGGER->msg("error","MSG from object: ".clreqdxml->error);
	exit 1;
}
$::LOGGER->msg("msg","Initialised 'cluster request daemon interface' object successfully.");

$::COMMON=new common_requests(timeout=>$::ARG_timeout,parallel=>$::ARG_parallel,clreqdxml=>$::CLREQD);
if(!defined($::COMMON)) {
	$::LOGGER->msg("error","Unable to initialise 'common requests' object.");
	exit 1;
}

$::LOGGER->msg("msg","Initialised 'common requests' object successfully.");


# At this point we've got to do some real work!!

{
my @DAEMONS=("lha_hbd","lha_statd","lha_lockd","lha_clusterd","lha_netd","lha_syncd","lha_mond");
my $cdaemon;
	for $cdaemon (@DAEMONS) {
		#########################################################
		# Before attempting to allocate a new port see if the	#
		# config database already has one...			#
		#########################################################
		my ($list,$err)=$::COMMON->run_config_sql(sql =>"SELECT number FROM ports WHERE detail = '$cdaemon';",force=>$::ARG_force);
		#########################################################
		# If all hosts return undef values then no value is	#
		# set. If certain hosts return values double check they	#
		# are all the same and if not abort!!			#
		#########################################################

		if(!defined($list)) {
			$::LOGGER->msg("error"," [$err]");
			exit 2;
		}
		
		my $chost;
		my $cport=undef;
		for $chost (sort(keys(%$list))) {
			if(!defined($list->{$chost})) {
				next;
			}
			$list->{$chost}=~s/^number\s+//;
			$list->{$chost}=int($list->{$chost});
			if(defined($cport) && $list->{$chost} != $cport) {
				$::LOGGER->msg("error","Port mismatch for daemon '$cdaemon' - cleaning up...");

				my ($list2,$err2)=$::COMMON->run_config_sql(sql =>"UPDATE ports SET detail = '',type = '' WHERE detail = '$cdaemon';",force=>$::ARG_force);
				$cport=undef; last;
			}
			$cport=$list->{$chost};
		}		

		if(defined($cport)) {
			$::LOGGER->msg("msg","Port for '$cdaemon' already defined as '$cport'.");
			next;
		}

		#########################################################
		# A port is requird, so we allocate it now...		#
		#########################################################

		($list,$err)=$::COMMON->get_common_free_port(count=>1,force=>$::ARG_force);
		if(!defined($list)) {
			$::LOGGER->msg("error","No results returned from 'get_common_free_port' call.");
			$::LOGGER->msg("error"," [$err]");
			exit 2;
		}
		#########################################################
		# We have a port so allocate it to the nodes..		#
		#########################################################
		$cport=$list->[0];
		($list,$err)=$::COMMON->run_config_sql(sql =>"UPDATE ports SET detail = '$cdaemon',type = 'daemon' WHERE number = '$cport';",force=>$::ARG_force);
		if(defined($err)) {
			$::LOGGER->msg("error","Unable to allocate port: '$err'.");
		} else {
			$::LOGGER->msg("msg","Allocated port '$cport' for daemon '$cdaemon'.");
		}
	}
}
exit(0);
